package com.yunji.coupon.service;

import com.google.common.collect.Lists;
import com.yunji.common.exception.ServiceException;
import com.yunji.common.utils.ConvertDataUtil;
import com.yunji.coupon.bo.CouponBO;
import com.yunji.coupon.bo.CouponReceiveTimesBO;
import com.yunji.coupon.bo.CouponRunningTmplBO;
import com.yunji.coupon.bo.ModifyCouponStatusBO;
import com.yunji.coupon.dao.*;
import com.yunji.coupon.doamin.*;
import com.yunji.coupon.dto.*;
import com.yunji.coupon.engine.CouponConvert;
import com.yunji.coupon.vo.CouponTmplListVO;
import com.yunji.coupon.vo.CouponVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : peter-zhu
 * @date : 2024/10/21 10:37
 * @description : 优惠券数据服务
 **/
@Slf4j
@Service
public class CouponService {

    @Resource
    private ICouponTmplDao couponTmplDao;
    @Resource
    private ICouponSiteDao couponSiteDao;
    @Resource
    private ICouponDao couponDao;
    @Resource
    private ICouponOrderDao couponOrderDao;
    @Resource
    private ICouponRecordDao couponRecordDao;


    public boolean addRecord(CouponRecordDO couponRecord) {
        return couponRecordDao.save(couponRecord);
    }


    public Set<String> likeNames(String name) {
        return couponTmplDao.likeNames(name);
    }

    public List<CouponTmplListVO> tmplPageList(CouponTmplPageDTO dto) {
        List<CouponTmplDO> list = couponTmplDao.tmplPageList(dto);
        return CouponConvert.tmpl2VOList(list);
    }

    public List<CouponTmplListVO> tmplList(CouponTmplPageDTO dto) {
        List<CouponTmplDO> list = couponTmplDao.tmplList(dto);
        return CouponConvert.tmpl2VOList(list);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean createCouponTmpl(CouponTmplDO domain, List<CouponSiteDO> siteList) {
        boolean save = couponTmplDao.save(domain);
        if (!CollectionUtils.isEmpty(siteList)) {
            siteList.forEach(a -> a.setCouponTmplId(domain.getCouponTmplId()));
            couponSiteDao.saveBatch(siteList);
        }
        return save;
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean modifyCouponTmpl(CouponTmplDO domain, List<CouponSiteDO> siteList) {
        if (domain.getCouponTmplId() == null) {
            return false;
        }
        if (!CollectionUtils.isEmpty(siteList)) {
            siteList.forEach(a -> a.setCouponTmplId(domain.getCouponTmplId()));
            couponSiteDao.removeByTmpId(domain.getCouponTmplId());
            couponSiteDao.saveBatch(siteList);
        }
        return couponTmplDao.updateById(domain);
    }

    public CouponTmplDO getCouponTmplDomain(Long id) {
        return couponTmplDao.getById(id);
    }

    public List<CouponTmplDO> getTmplListByIds(List<Long> ids) {
        return couponTmplDao.listByIds(ids);
    }

    public CouponTmplCreateDTO getCouponTmpl(Long id) {
        CouponTmplDO byId = couponTmplDao.getById(id);
        CouponTmplCreateDTO createDTO = new CouponTmplCreateDTO();
        BeanUtils.copyProperties(byId, createDTO);
        List<CouponSiteDO> domains = couponSiteDao.listByTmplId(id);
        List<CouponSiteDTO> couponSiteDTOS = CouponConvert.sites2DTOList(domains);
        createDTO.setCouponSiteList(couponSiteDTOS);
        return createDTO;
    }

    public boolean removeCouponTmpl(Long id) {
        return couponTmplDao.removeById(id);
    }

    public boolean isReceive(Long id) {
        return couponTmplDao.isReceive(id);
    }

    public boolean modifyReceiveTime(Long id, Integer action) {
        return couponTmplDao.modifyReceiveTime(id, action);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean modifyRecall(Long id) {
        boolean recall = couponDao.recall(id);
        boolean b = couponTmplDao.modifyRecall(id);
        boolean rs = recall && b;
        if (rs)
            return true;
        else
            throw new ServiceException("已被撤回或没有优惠券");
    }

    public List<CouponTmplDO> runningTmplList(CouponRunningTmplBO bo) {
        return couponTmplDao.runningTmplList(bo);
    }

    public Integer receiveTimes(CouponReceiveTimesBO bo) {
        return couponDao.receiveTimes(bo);
    }

    @Transactional(rollbackFor = Exception.class)
    public boolean receiveCoupon(CouponBO bo) {
        CouponDO couponDO = new CouponDO();
        BeanUtils.copyProperties(bo.getDomain(), couponDO);
        boolean b = couponTmplDao.receiveCoupon(couponDO.getCouponTmplId());
        if (b)
            couponDao.save(couponDO);
        bo.getDomain().setCouponId(couponDO.getCouponId());
        return b;
    }

    public List<CouponDO> couponDOList(CouponListDTO dto) {
        return couponDao.couponList(dto);
    }

    public List<CouponVO> couponList(CouponListDTO dto) {
        List<CouponDO> couponDOS = couponDao.couponList(dto);
        return getCountVOList(couponDOS);
    }

    public List<CouponVO> couponList(CouponPageDTO dto) {
        List<CouponDO> couponDOS = couponDao.couponList(dto);
        return getCountVOList(couponDOS);
    }

    private List<CouponVO> getCountVOList(List<CouponDO> couponDOS) {
        couponDOS = couponDOS.stream().sorted(Comparator.comparing(CouponDO::getValidPeriodE))
                .collect(Collectors.toList());
        List<CouponVO> voList = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(couponDOS)) {
            Set<Long> longs = ConvertDataUtil.set2FieldList(couponDOS, CouponDO::getCouponTmplId);
            Map<Long, CouponTmplDO> dataMap =
                    ConvertDataUtil.getDataMap(() -> couponTmplDao.listByIds(Lists.newArrayList(longs)), CouponTmplDO::getCouponTmplId);
            for (CouponDO couponDO : couponDOS) {
                CouponBO couponBO = new CouponBO(dataMap.get(couponDO.getCouponTmplId()), couponDO);
                voList.add(CouponConvert.convert2CouponVO(couponBO));
            }
        }
        return voList;
    }

    public List<CouponVO> couponPageList(CouponPageDTO dto) {
        List<CouponDO> couponDOS = couponDao.couponPageList(dto);
        return getCountVOList(couponDOS);
    }

    public CouponDO getById(Long id) {
        return couponDao.getById(id);
    }

    public CouponBO getCouponBO(Long id) {
        CouponDO couponDO = couponDao.getById(id);
        Optional.ofNullable(couponDO).orElseThrow(() -> {
            String msg = "CouponService getCouponBO 优惠券不存在,id为" + id;
            log.error(msg);
            return new ServiceException(msg);
        });
        CouponTmplDO tmpl = couponTmplDao.getById(couponDO.getCouponTmplId());
        Optional.ofNullable(tmpl).orElseThrow(() -> {
            String tmplMsg = "CouponService getCouponBO 优惠券模板的id不存在,优惠券id为" + id + ",模板id为" + couponDO.getCouponTmplId();
            log.error(tmplMsg);
            return new ServiceException(tmplMsg);
        });
        return new CouponBO(tmpl, couponDO);
    }

    public boolean saveOrderCoupon(CouponOrderDO domain) {
        return couponOrderDao.save(domain);
    }

    public boolean modifyCouponStatus(ModifyCouponStatusBO status) {
        return couponDao.modifyCouponStatus(status);
    }

    public boolean removeOrderCoupon(Long orderId) {
        return couponOrderDao.removeOrderCoupon(orderId);
    }

    public Long getCidByOid(Long orderId) {
        return couponOrderDao.getCidByOid(orderId);
    }

    public String getCouponNameByOid(Long orderId) {
        Long cidByOid = couponOrderDao.getCidByOid(orderId);
        if (cidByOid != null) {
            CouponDO couponDO = couponDao.getById(cidByOid);
            Long couponTmplId = couponDO.getCouponTmplId();
            return Optional.ofNullable(couponTmplDao.getById(couponTmplId)).map(CouponTmplDO::getCouponName).orElse(null);
        }
        return null;
    }

    public List<CouponSiteDO> getAvailableSites(Long siteId, List<Long> tmplIdList) {
        return couponSiteDao.getAvailableSites(siteId, tmplIdList);
    }

    public List<CouponSiteDO> getAvailableSites(Long siteId, Long tmplId) {
        return couponSiteDao.getAvailableSites(siteId, tmplId);
    }

    public boolean modifyUsedCount(Long couponTmplId, Integer count) {
        return couponTmplDao.modifyUsedCount(couponTmplId, count);
    }

    public boolean existsByCid(Long couponId) {
        return couponOrderDao.existsByCid(couponId);
    }
}
